package cn.zyl.demo.common.tools;

import cn.zyl.demo.common.enums.CharsetEnum;
import cn.zyl.demo.common.enums.CypherNameEnum;
import cn.zyl.demo.common.exception.ToolsException;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/** CypherResTool工具类说明:非对称加密算法 RSA
 * @author wsz
 * @date 2020-07-24
 * @version v1.0
 */
public class CypherRsaTool {
    private CypherRsaTool(){throw new IllegalAccessError("CypherRsaTool");}
    /** 密钥长度 */
    private static final int NUM_1024 = 1024;
    /** RSA最大加密明文大小 */
    private static final int MAX_ENCRYPT = 117;
    /**  RSA最大解密密文大小 */
    private static final int MAX_DECRYPT = 128;
    /**
     * 获取密钥对象
     * @return 密钥对象
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance(CypherNameEnum.Q_RSA.getName());
        generator.initialize(NUM_1024);
        return generator.generateKeyPair();
    }
    /**
     * 获取私钥字符串
     * @param keyPair 密钥对象
     * @return 私钥字符串
     */
    public static String getPrivateKey(KeyPair keyPair){
        return Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
    }
    /**
     * 获取公钥字符串
     * @param keyPair 密钥对象
     * @return 公钥字符串
     */
    public static String getPublicKey(KeyPair keyPair){
        return Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
    }
    /**
     * 获取私钥处理对象
     * @param privateKey 私钥字符串
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        //密钥,解码
        byte[] decodedKey = Base64.getDecoder().decode(privateKey);
        //创建使用给定的编码密钥一个新的PKCS8EncodedKeySpec
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        //返回的KeyFactory对象转换公/指定算法的私钥
        return KeyFactory.getInstance(CypherNameEnum.Q_RSA.getName()).generatePrivate(keySpec);
    }
    /**
     * 获取公钥处理对象
     * @param publicKey 公钥字符串
     * @return 公钥对象
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return KeyFactory.getInstance(CypherNameEnum.Q_RSA.getName()).generatePublic(keySpec);
    }

    /**
     * RSA加密
     * @param data 待加密数据
     * @param publicKey 公钥字符
     * @return 密文
     */
    public static String encrypt(String data, String publicKey) throws Exception {
        return encrypt(data, getPublicKey(publicKey));
    }
    /**
     * RSA加密
     * @param data 待加密数据
     * @param publicKey 公钥对象
     * @return 密文
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(CypherNameEnum.Q_RSA.getName());
        //加密
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = subsectionHandle(cipher,data.getBytes(), MAX_ENCRYPT);
        if(null == encryptedData){return "";}
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串.加密后的字符串
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    /**
     * RSA解密
     * @param data 待解密数据
     * @param privateKey 私钥字符
     * @return 明文
     */
    public static String decrypt(String data, String privateKey) throws Exception {
        return decrypt(data, getPrivateKey(privateKey));
    }
    /**
     * RSA解密
     * @param data 待解密数据
     * @param privateKey 私钥对象
     * @return 明文
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(CypherNameEnum.Q_RSA.getName());
        //解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = subsectionHandle(cipher,Base64.getDecoder().decode(data), MAX_DECRYPT);
        if(null == decryptedData){return "";}
        // 解密后的内容
        return new String(decryptedData, CharsetEnum.UTF_8.getType());
    }
    /**
     * 加解密中间分段处理-公共部分
     * @param cipher 加密,解密的对象
     * @param dataBytes 待处理内容，字节数组
     * @return 处理完成的内容
     * @throws ToolsException 加解密发生的异常
     */
    private static byte[] subsectionHandle(Cipher cipher,byte[] dataBytes,int maxBlock)throws ToolsException {
        byte[] cache , decryptedData;
        try(ByteArrayOutputStream out= new ByteArrayOutputStream()){
            // 对数据分段解密
            for(int offset = 0,i = 1,len = dataBytes.length;len - offset > 0;i++){
                if (len - offset > maxBlock) {
                    cache = cipher.doFinal(dataBytes, offset, maxBlock);
                } else {
                    cache = cipher.doFinal(dataBytes, offset, len - offset);
                }
                out.write(cache, 0, cache.length);
                offset = i * maxBlock;
            }
            decryptedData = out.toByteArray();
        }catch (Exception e){
            throw new ToolsException("rsa 加解密 处理失败",e);
        }
        return decryptedData;
    }
    public static void main(String[] args) {
        try {
            KeyPair keyPair = getKeyPair();
            String publicKey = getPublicKey(keyPair);
            System.out.println("公钥："+publicKey);
            String privateKey = getPrivateKey(keyPair);
            System.out.println("私钥："+privateKey);
            String word  = "123456";
            String pass = encrypt(word, publicKey);
            System.out.println("密文"+pass);
            String msg = decrypt(pass, privateKey);
            System.out.println(msg);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}